package ie.dkit.java3Demulation.gui;

import ie.dkit.java3Demulation.drawer.Circle3DDrawable;
import ie.dkit.java3Demulation.drawer.Circle3DDrawer;
import ie.dkit.java3Demulation.drawer.CircleDrawable;
import ie.dkit.java3Demulation.drawer.CircleDrawer;
import ie.dkit.java3Demulation.drawer.Drawable;
import ie.dkit.java3Demulation.drawer.Line3DDrawable;
import ie.dkit.java3Demulation.drawer.Line3DDrawer;
import ie.dkit.java3Demulation.drawer.LineDrawable;
import ie.dkit.java3Demulation.drawer.LineDrawer;
import ie.dkit.java3Demulation.drawer.EllipseDrawer;
import ie.dkit.java3Demulation.drawer.EllipseDrawable;
import ie.dkit.java3Demulation.objects.Line;
import ie.dkit.java3Demulation.objects.Circle;
import ie.dkit.java3Demulation.objects.Ellipse;
import ie.dkit.java3Demulation.objects3d.Circle3D;
import ie.dkit.java3Demulation.objects3d.Line3D;
import ie.dkit.java3Demulation.transforming.Transformer;

import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.List;
import java.util.Vector;

public class DisplayCanvas extends Canvas implements MouseMotionListener,
		MouseListener {

	/**
	 * 
	 */
	private static final long serialVersionUID = -1317321045934348184L;

	private static final int FRAMERATE = 25;

	private List<Drawable> drawables = new Vector<Drawable>();

	private LineDrawer lineDrawer = new LineDrawer(getGraphics(),
			getWidth() / 2, getHeight() / 2);

	private Line3DDrawer line3DDrawer = new Line3DDrawer(getGraphics(),
			getWidth() / 2, getHeight() / 2);

	private CircleDrawer circleDrawer = new CircleDrawer(getGraphics(),
			getWidth() / 2, getHeight() / 2);

	private EllipseDrawer eclipseDrawer = new EllipseDrawer(getGraphics(),
			getWidth() / 2, getHeight() / 2);

	private Circle3DDrawer circle3DDrawer = new Circle3DDrawer(getGraphics(),
			getWidth() / 2, getHeight() / 2);

	private GUI gui;

	public DisplayCanvas(GUI gui) {
		this.gui = gui;
		addMouseMotionListener(this);
		addMouseListener(this);
	}

	public void add(Drawable drawable) {
		drawables.add(drawable);
	}

	public void remove(Drawable drawable) {
		drawables.remove(drawable);
	}

	public void paint(Graphics g) {
		Image doubleBuffer = createImage(getSize().width, getSize().height);
		Graphics currentDoubleBufferGraphics = doubleBuffer.getGraphics();
		lineDrawer.setGraphics(currentDoubleBufferGraphics, getWidth() / 2,
				getHeight() / 2);
		line3DDrawer.setGraphics(currentDoubleBufferGraphics, getWidth() / 2,
				getHeight() / 2);
		circleDrawer.setGraphics(currentDoubleBufferGraphics, getWidth() / 2,
				getHeight() / 2);
		eclipseDrawer.setGraphics(currentDoubleBufferGraphics, getWidth() / 2,
				getHeight() / 2);
		circle3DDrawer.setGraphics(currentDoubleBufferGraphics, getWidth() / 2,
				getHeight() / 2);
		for (Drawable drawable : drawables) {
			if (drawable instanceof LineDrawable) {
				LineDrawable lineDrawable = (LineDrawable) drawable;
				for (Line line : lineDrawable.getLines()) {
					lineDrawer.draw(line);
				}
			}
			if (drawable instanceof Line3DDrawable) {
				Line3DDrawable lineDrawable = (Line3DDrawable) drawable;
				for (Line3D line : lineDrawable.getLines()) {
					line3DDrawer.draw(line);
				}
			}
			if (drawable instanceof CircleDrawable) {
				CircleDrawable circleDrawable = (CircleDrawable) drawable;
				for (Circle circle : circleDrawable.getCircles()) {
					circleDrawer.draw(circle);
				}
			}
			if (drawable instanceof EllipseDrawable) {
				EllipseDrawable eclipseDrawable = (EllipseDrawable) drawable;
				for (Ellipse eclipse : eclipseDrawable.getEclipses()) {
					eclipseDrawer.draw(eclipse);
				}
			}
			if (drawable instanceof Circle3DDrawable) {
				Circle3DDrawable circle3DDrawable = (Circle3DDrawable) drawable;
				for (Circle3D circle3D : circle3DDrawable.getCircle3Ds()) {
					circle3DDrawer.draw(circle3D);
				}
			}
		}
		g.drawImage(doubleBuffer, 1, 1, this);
		try {
			Thread.sleep(1000 / FRAMERATE);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		repaint();
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see java.awt.Canvas#update(java.awt.Graphics)
	 */
	public void update(Graphics g) {
		paint(g);
	}

	public void setPerspective(boolean enabled) {
		line3DDrawer.setPerpective(enabled);
	}

	private int mouseX, mouseY;

	public void mouseDragged(MouseEvent e) {
		if (e.getModifiers() == 19) {
			// Ctrl + Shift
			// Scale
			if (mouseX - e.getX() != 0) {
				if (mouseX - e.getX() > 0) {
					Transformer.scale(gui.transformables, 0.95, 0.95, 0.95);
				} else {
					Transformer.scale(gui.transformables, 1.05, 1.05, 1.05);
				}
			}
		}
		if (e.getModifiers() == 17) {
			// Shift
			// Scale
			if (mouseX - e.getX() != 0) {
				if (mouseX - e.getX() > 0) {
					Transformer.scale(gui.transformables, 0.95, 1, 1);
				} else {
					Transformer.scale(gui.transformables, 1.05, 1, 1);
				}
			}
			if (mouseY - e.getY() != 0) {
				if (mouseY - e.getY() < 0) {
					Transformer.scale(gui.transformables, 1, 0.95, 1);
				} else {
					Transformer.scale(gui.transformables, 1, 1.05, 1);
				}
			}
			mouseX = e.getX();
			mouseY = e.getY();
		}
		if (e.getModifiers() == 18) {
			// CTRL
			// Move
			if (mouseX - e.getX() != 0) {
				Transformer.translate(gui.transformables, -(mouseX - e.getX()),
						0, 0);
			}
			if (mouseY - e.getY() != 0) {
				Transformer.translate(gui.transformables, 0, mouseY - e.getY(),
						0);
			}
		}
		if (e.getModifiers() == 16) {
			// Normal No button
			// Rotate
			if (mouseX - e.getX() != 0) {
				Transformer.rotateY(gui.transformables, mouseX - e.getX());
			}
			if (mouseY - e.getY() != 0) {
				Transformer.rotateX(gui.transformables, mouseY - e.getY());
			}
		}
		mouseX = e.getX();
		mouseY = e.getY();
	}

	public void mouseMoved(MouseEvent e) {
		gui.positionLabel.setText("Position: " + e.getX() + "|" + e.getY());
	}

	public void mouseClicked(MouseEvent e) {
	}

	public void mouseEntered(MouseEvent e) {
	}

	public void mouseExited(MouseEvent e) {
	}

	public void mousePressed(MouseEvent e) {
		mouseX = e.getX();
		mouseY = e.getY();
	}

	public void mouseReleased(MouseEvent e) {
		mouseX = mouseY = 0;
	}
}
